# [十七] Spring MVC 核心调用流程-返回值解析

# 返回值解析

概述

当反射调用成功后,有可能方法会有返回值,而返回值处理也是一个比较重要的事情,根据什么样的方式把返回值响应回去,返回值响应时有可能是数据有可能是界面,如果返回数据的话,要把返回值解析成对应的格式,例如如果返回值是一个 list 对象,就需要解析这个 list 对象把 list 对象解析成 json 格式。返回值解析讨论跟入参解析基本上类似。

返回值解析原理 :

1、把返回值封装成对象,对象跟 MethodParameter对象差不多,里面包括参数名称、类型、参数注解等等信息

2、根据返回值类型用策略模式找到一个解析类

3、用这个解析类解析

4、这块跟两个比较典型的就差不多了,一个是带@ResponseBody 注解的,一个是直接返回字符串响应一个界面的,里面涉及到一个ModelAndViewContainer 容器,这个容器会把视图名称设置到里面,已经 Model 数据,就是响应到界面的数据也会放到这个容器中

进入invokeHandlerMethod方法

所在类:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		try {
			// Model工厂,收集了@ModelAttribute注解的方法
			ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
			// ...... 省略
			ModelAndViewContainer mavContainer = new ModelAndViewContainer();
			// 调用有 @ModelAttribute注解的方法。每次请求都会调用有 @ModelAttribute
			// 注解的方法,把 @ModelAttribute注解的方法的返回值存储
			// 到ModelAndViewContainer对象的 map中了
			modelFactory.initModel(webRequest, mavContainer, invocableMethod);
			mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

			// ...... 省略
			// Controller方法调用,重点看看
			invocableMethod.invokeAndHandle(webRequest, mavContainer);
			if (asyncManager.isConcurrentHandlingStarted()) {
				return null;
			}
			// 返回业务处理,返回json格式还是跳转页面
			return getModelAndView(mavContainer, modelFactory, webRequest);
		}
		finally {
			webRequest.requestCompleted();
		}
	}

进入invokeAndHandle方法

所在类:org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
		// 具体调用逻辑,重点看
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
		setResponseStatus(webRequest);
		// 设置有 @ResponseBody 标注的 请求处理为 true
		if (returnValue == null) {
			if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
				mavContainer.setRequestHandled(true);
				return;
			}
		}
		else if (StringUtils.hasText(getResponseStatusReason())) {
			mavContainer.setRequestHandled(true);
			return;
		}
		// 设置没有 @ResponseBody 标注的 请求处理为 false,返回处理的时候根据这个属性判断
		// 是否返回页面 还是 Json 格式
		mavContainer.setRequestHandled(false);
		Assert.state(this.returnValueHandlers != null, "No return value handlers");
		try {
			// 返回值处理业务,重点看
			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
		catch (Exception ex) {
			if (logger.isTraceEnabled()) {
				logger.trace(formatErrorForReturnValue(returnValue), ex);
			}
			throw ex;
		}
	}

进入handleReturnValue方法

所在类:org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
		// 找到一个合适的返回值处理方法,匹配过程
		HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
		if (handler == null) {
			throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
		}
		// 具体处理返回值业务,由子类实现,默认有15种返回值处理类
		handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
	}

知识点

handleReturnValue方法种有一个mavContainer参数,这个参数类型是ModelAndViewContainer,内部维护了一个ModelMap用于存放@ModelAttribute注解的方法的返回值。比如:

 /*
  * TODO:所有的请求都会先调用这个方法,把方法执行的返回值存到 areaBean 的map容器中。
  * */
  @ModelAttribute("areaBean")
  public List<ConsultConfigArea> queryArea(@RequestParam String areaCode) {
        Map map = new HashMap<>();
        map.put("areaCode",areaCode);
        return areaService.queryAreaFromDB(map);
    }
/*
 * TODO:@ModelAttribute 来进行初始化赋值
 * */
 public @ResponseBody String checkArea (@ModelAttribute("areaBean") ConsultConfigArea area) {
 // 通过@ModelAttribute 拿到缓存中名字为 areaBean的对象 赋值给 area,
 // 然后就可以直接调用 area.getXX() 获取到值了
 System.out.println("=======业务处理======="+area.getAreaCode(); 
 }

进入selectHandler方法

所在类:org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite

	private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
		boolean isAsyncValue = isAsyncReturnValue(value, returnType);
		for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
			if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
				continue;
			}
			// 典型的策略模式
			if (handler.supportsReturnType(returnType)) {
				return handler;
			}
		}
		return null;
	}

知识点

HandlerMethodReturnValueHandler接口也是典型的策略模式,和参数解析的流程基本一致,也有一个supportsReturnType匹配方法和handleReturnValue处理返回值业务的方法。

返回invokeHandlerMethod方法

进入getModelAndView方法

所在类:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
			ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

		modelFactory.updateModel(webRequest, mavContainer);
		// 判断 ModelAndViewContainer 类中的 requestHandled 属性是否为 true,
		// 如果有 @ResponseBody 则直接返回空,不需要跳转页面
		if (mavContainer.isRequestHandled()) {
			return null;
		}
		// 否则处理跳转页面
		ModelMap model = mavContainer.getModel();
		ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
		if (!mavContainer.isViewReference()) {
			mav.setView((View) mavContainer.getView());
		}
		if (model instanceof RedirectAttributes) {
			Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
			HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
			if (request != null) {
				RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
			}
		}
		return mav;
	}

扩展阅读

HandlerMethodReturnValueHandler返回值解析器,结构和参数解析器基本一致,Spring中默认有15种返回值解析器,来对应完成某种返回值的解析工作。添加过程是Spring MVC启动实例化后,通过RequestMappingHandlerAdapter类的afterPropertiesSet方法调用getDefaultReturnValueHandlers添加到HandlerMethodReturnValueHandler解析器中的。

  • getDefaultReturnValueHandlers方法源码
private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
		List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>();

		// Single-purpose return value types
		// 默认基本类型的返回值
		// 处理返回类型 ModelAndView
		handlers.add(new ModelAndViewMethodReturnValueHandler());
		// 处理返回类型 Model
		handlers.add(new ModelMethodProcessor());
		// 处理返回类型 View
		handlers.add(new ViewMethodReturnValueHandler());
		// 处理返回类型 ResponseBodyEmitter
		handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),
				this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));
		// 处理返回类型 StreamingResponseBody
		handlers.add(new StreamingResponseBodyReturnValueHandler());
		// 处理返回类型 HttpEntity 和 RequestEntity
		handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
				this.contentNegotiationManager, this.requestResponseBodyAdvice));
		// 处理返回类型 HttpHeaders
		handlers.add(new HttpHeadersReturnValueHandler());
		// 处理返回类型 Callable
		handlers.add(new CallableMethodReturnValueHandler());
		// 处理返回类型DeferredRequest 和 ListenableFuture
		handlers.add(new DeferredResultMethodReturnValueHandler());
		// 处理返回类型WebAsyncTask
		handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));

		// Annotation-based return value types
		// 带注解的返回值类型
		// 处理 @ModelAttribute
		handlers.add(new ModelAttributeMethodProcessor(false));
		// 处理 @ResponseBody
		handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
				this.contentNegotiationManager, this.requestResponseBodyAdvice));

		// Multi-purpose return value types
		// 处理返回类型 Void.class 和 CharSequense.class
		handlers.add(new ViewNameMethodReturnValueHandler());
		// 处理返回类型 Map
		handlers.add(new MapMethodProcessor());

		// Custom return value types
		// 自定义返回值类型
		if (getCustomReturnValueHandlers() != null) {
			handlers.addAll(getCustomReturnValueHandlers());
		}

		// Catch-all
		if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
			handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
		}
		else {
			handlers.add(new ModelAttributeMethodProcessor(true));
		}

		return handlers;
	}

# 异常解析

概述

类上面加上@ControllerAdvice("com.xx.xx")注解,这个包定义就是只对这个包里 面的Controller生效。然后类里面的方法加上

  • @ExceptionHandler({ArrayIndexOutOfBoundsException.class})
  • @ExceptionHandler({NullPointerException.class})

表示这个方法当调用过程中出现注解里面定义的异常时会被调用到,这些方法就是对异常处理的方法。

@ControllerAdvice原理

1、收集注解包装成类

2、建立@ExceptionHandler 中异常和 Method 的映射关系

3、根据出现的异常从映射关系中找到对应的 Method 对象

4、反射调用,这个调用逻辑跟 Controller 里面具体方法调用逻辑一模一样

为什么要把 ModelAndViewContainer mavContainer 做为参数放到返回值解析中?

ServletInvocableHandlerMethod类的invokeAndHandle处理方法调用的时候中会设置一个属性,如果有@ResponseBody注解的标注 mavContainer.setRequestHandled(true);,否则设置为 mavContainer.setRequestHandled(false); 这样调用getModelAndView返回业务处理的时候就可以根据ModelAndViewContainer类中的requestHandled属性来判断是否返回json格式还是返回页面。